﻿namespace Oddo.Concept.Crm.Common.ViewModels
{
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq.Expressions;
    using System.Threading;
    using System.Windows.Input;
    using Microsoft.Practices.Prism.Commands;
    using Microsoft.Practices.Prism.UnityExtensions;
    using Microsoft.Practices.ServiceLocation;
    using Microsoft.Practices.Unity;
    using Oddo.Concept.Crm.Common.Communication.Interfaces;
    using Oddo.Concept.Crm.Common.Extensions;
    using Oddo.Concept.Crm.Common.Interfaces;
    using Oddo.Concept.Crm.Common.Models;

    /// <summary>
    /// Classe de base pour un ViewModel
    /// </summary>
    public abstract class ViewModelBase : IViewModel, INotifyPropertyChangedEx
    {
        private bool _shouldNotifyPropertyChanged = true;
        private bool _isBusy;
        private bool _canCancelOperation = true;
       
        /// <summary>
        /// Evenement declenché lors du changement d'une propriété
        /// </summary>
        [field: NonSerialized]
        public virtual event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Obtient le container d'injection de dependance Unity
        /// </summary>
        public IUnityContainer Container
        {
            get { return ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer; }
        }

        /// <summary>
        /// Determines si une operation est en cours
        /// </summary>
        public virtual bool IsBusy
        {
            get { return _isBusy; }
            set { _isBusy = value; NotifyPropertyChanged(()=>IsBusy); }
        }
        /// <summary>
        /// Determines si l'annulation d'une operation est possible. True par défaut
        /// </summary>
        public virtual bool CanCancelOperation
        {
            get { return _canCancelOperation; }
            set { _canCancelOperation = value; NotifyPropertyChanged(() => CanCancelOperation); }
        }

        /// <summary>
        /// Obtient ou définit si l'evenement PropertyChanged doit être declenché
        /// </summary>
        public virtual bool ShouldNotifyPropertyChanged
        {
            get { return _shouldNotifyPropertyChanged; }
            set { _shouldNotifyPropertyChanged = value; }
        }

        /// <summary>
        /// Notifie le changement d'une propriété
        /// </summary>
        /// <param name="propertyname">Le nom de la propriété modifiée</param>
        /// <remarks>La valeur de la propriété ShouldNotifyPropertyChanged est ignoré</remarks>
        public void NotifyPropertyChanged(string propertyname)
        {
            var handler = PropertyChanged;
            if(handler !=null)
                handler(this, new PropertyChangedEventArgs(propertyname));
        }
        /// <summary>
        /// Notifie le changement d'une propriété si la valeur de la propriété ShouldNotifyPropertyChanged vaut True
        /// </summary>
        protected internal void NotifyPropertyChanged<T>(Expression<Func<T>> propertyExpression)
        {
            this.RaisePropertyChanged(propertyExpression);
        }
        /// <summary>
        /// Notifie le changement de toutes les propriétés.
        /// </summary>
        /// <remarks>La valeur de la propriété ShouldNotifyPropertyChanged est ignorée</remarks>
        protected internal void NotifyPropertiesChanged()
        {
            NotifyPropertyChanged(string.Empty);
        }
    }

    /// <summary>
    /// ViewModel de base typé
    /// </summary>
    /// <typeparam name="T">Type implementant l'interface IBusinessModel</typeparam>
    public abstract class ViewModelBase<T> : ViewModelBase where T : IBusinessModel
    {
        private ObservableCollection<T> _values;
        private ICommand _searchCommand;
        private ICommand _resetCommand;
        private ICommand _addCommand;
        private ICommand _editCommand;
        private ICommand _deleteCommand;
        private ICommand _cancelCommand;

        private IFindRequest<T> _findRequest;
        private CancellationTokenSource _cancellationTokenSource;

        protected ViewModelBase()
        {
            _values = new ObservableCollection<T>();
        }

        public virtual IFindRequest<T> CreateFindRequest()
        {
            return base.Container.TryResolve<IFindRequest<T>>();
        }

        public virtual IFindRequest<T> FindRequest
        {
            get { return _findRequest ?? ( _findRequest = CreateFindRequest() ); }
            private set { _findRequest = value; }
        }

        public virtual ICommand SearchCommand
        {
            get { return _searchCommand ?? (_searchCommand = new DelegateCommand(SearchCommandHandler, CanSearch)); }
            private set { _searchCommand = value; }
        }

        public virtual ICommand ResetCriteriasCommand
        {
            get { return _resetCommand ?? (_resetCommand = new DelegateCommand(ResetCriteriasCommandHandler, CanResetCriterias)); }
            private set { _resetCommand = value; }
        }

        public virtual ICommand AddCommand
        {
            get { return _addCommand??(_addCommand = new DelegateCommand(AddCommandHandler, CanAdd)); }
            private set { _addCommand = value; }
        }

        public virtual ICommand EditCommand
        {
            get { return _editCommand ?? (_editCommand = new DelegateCommand(EditCommandHandler, CanEdit)); }
            private set { _editCommand = value; }
        }

        public virtual ICommand DeleteCommand
        {
            get { return _deleteCommand ?? (_deleteCommand = new DelegateCommand(DeleteCommandHandler, CanDelete)); }
            private set { _deleteCommand = value; }
        }

        public virtual ICommand CancelCommand
        {
            get { return _cancelCommand ?? (_cancelCommand = new DelegateCommand(CancelCommandHandler, CanCancel)); }
            private set { _cancelCommand = value; }
        }

        /// <summary>
        /// Obtient ou définit la collection observable de type T
        /// </summary>
        public virtual ObservableCollection<T> Values
        {
            get { return _values; }
            set
            {
                _values = value;
                NotifyPropertyChanged(() => Values);
            }
        }

        protected internal CancellationTokenSource CancellationTokenSource
        {
            get { return _cancellationTokenSource; }
        }

        protected abstract void SearchCommandHandler();
        protected abstract void ResetCriteriasCommandHandler();
        protected abstract void AddCommandHandler();
        protected abstract void EditCommandHandler();
        protected abstract void DeleteCommandHandler();

        protected virtual void CancelCommandHandler()
        {
            if(_cancellationTokenSource!=null)
                _cancellationTokenSource.Cancel(true);
        }

        protected virtual bool CanCancel()
        {
            return CanCancelOperation;
        }

        protected virtual bool CanSearch()
        {
            return true;
        }

        protected virtual bool CanResetCriterias()
        {
            return true;
        }

        protected virtual bool CanAdd()
        {
            return true;
        }

        protected virtual bool CanEdit()
        {
            return true;
        }

        protected virtual bool CanDelete()
        {
            return true;
        }
        
        public virtual void InitializeCancellationTokenSource()
        {
            if(_cancellationTokenSource!=null)
                _cancellationTokenSource.Dispose();

            _cancellationTokenSource = new CancellationTokenSource();
            _cancellationTokenSource.Token.Register(() =>
                {
                    IsBusy = false;
                    Container.Resolve<IMessageDisplayServices>().ShowDiscret("Operation canceled");
                });
        }
    }
}